package cn.tedu.happygame;

import com.sun.rowset.internal.Row;

import javax.management.relation.Role;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Arrays;
import java.util.Random;

public class World extends JPanel {
    public static final int WIDTH = 429; //窗口的宽
    public static final int HEIGHT = 570; //窗口的高
    public static final int ROWS = 8; //8行
    public static final int COLS = 6; //6列
    public static final int ELEMENT_SIZE = 60; //元素大小
    public static final int OFFSET = 10; //偏移量(第1个元素距窗口左边缘的距离)
    private Element[][] elements = new Element[ROWS][COLS]; //元素数组(8行6列)

    //元素类型
    public static final int ELEMENT_TYPE_BEAR = 0; //熊
    public static final int ELEMENT_TYPE_BIRD = 1; //鸟
    public static final int ELEMENT_TYPE_FOX = 2; //狐狸
    public static final int ELEMENT_TYPE_FROG = 3; //青蛙


    /**
     * 创建元素(根据行列计算x/y坐标，而后创建对象)
     */
    public Element createElement(int rows, int cols) {
        int x = OFFSET + cols * ELEMENT_SIZE;
        int y = OFFSET + rows * ELEMENT_SIZE;
        Random random = new Random();
        int type = random.nextInt(4);
        if (type == ELEMENT_TYPE_BEAR) {
            return new Bear(x, y);
        } else if (type == ELEMENT_TYPE_BIRD) {
            return new Bear(x, y);
        } else if (type == ELEMENT_TYPE_FOX) {
            return new Fox(x, y);
        } else {
            return new Frog(x, y);
        }
    }

    //元素可消状态
    public static final int ELIMINATE_NON = 0; //不可消
    public static final int ELIMINATE_ROW = 1; //行可消
    public static final int ELIMINATE_COL = 2; //列可消

    /**
     * 检测元素是否是可消的 返回元素可消状态(0/1/2)
     */
    public int canEliminate(int row, int col) {
        Element e = elements[row][col]; //获取当前元素
        //判断横向
        if (col >= 2) {
            Element e1 = elements[row][col - 1]; //获取当前元素前面第1个元素
            Element e2 = elements[row][col - 2]; //获取当前元素前面第2个元素
            if (e != null && e1 != null && e2 != null) { //若元素都不是null
                //若当前元素与它前面的两个元素类型都相同
                if (e.getClass().equals(e1.getClass()) && e.getClass().equals(e2.getClass())) {
                    return ELIMINATE_ROW; //返回1，表示行可消
                }
            }
        }
        //判断纵向
        if (row >= 2) {
            Element e1 = elements[row - 1][col]; //获取当前元素上面的第1个元素(1,0)
            Element e2 = elements[row - 2][col]; //获取当前元素上面的第2个元素(0,0)
            if (e != null && e1 != null && e2 != null) { //若元素都不是null
                //若当前元素与它上面的两个元素的类型相同
                if (e.getClass().equals(e1.getClass()) && e.getClass().equals(e2.getClass())) {
                    return ELIMINATE_COL; //返回2，表示列可消
                }
            }
        }
        return ELIMINATE_NON; //返回0，表示不可消
    }


    /**
     * 填充所有元素
     */
    public void fillAllElements() {
        for (int row = 0; row < ROWS; row++) {
            for (int col = 0; col < COLS; col++) {
                do {
                    Element e = createElement(row, col); //创建元素
                    elements[row][col] = e; //将元素填充到elements数组中
                } while (canEliminate(row, col) != ELIMINATE_NON); //若可消则重新生成元素
            }
        }
    }






    /**
     * 判断两个元素是否能交换(相邻则能交换，否则不能交换)
     */
    public boolean isAdjacent() {
        if ((Math.abs(firstRow - secondRow) == 1 && firstCol == secondCol) || Math.abs(firstCol - secondCol) == 1 && firstRow == secondRow) {
            return true;//相邻
        }
        return false; //不相邻
    }


    /** 移动两个元素（动画移动） */
    public void moveElement(){
        if (firstRow == secondRow){ //若行号相同 表示左移动
            int firstX = OFFSET+firstCol*ELEMENT_SIZE;//第1个元素的x坐标
            int secondX = OFFSET+secondCol*ELEMENT_SIZE;//第2个元素的x坐标
            int step = firstX<secondX?4:-4; //布长（控制左右），值越大速度越快
            for (int i = 0; i < 15; i++) { //走15次（每次走4，共15*4为60）
                try { //每动一下休眠10毫秒
                    Thread.sleep(10);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                firstX+=step; //firstX加
                secondX-=step; //secondX减
                //修改元素坐标（刚刚只是在修改变量的值，一定是元素坐标修改之后次啊能看到移动）
                elements[firstRow][firstCol].setX(firstX);
                elements[secondRow][secondCol].setX(secondX);
                repaint();
            }
        }
        if (firstCol == secondCol){ //若列号相同 表示上下移动
            int firstY = OFFSET+firstRow*ELEMENT_SIZE;//第一个元素的y坐标
            int secondY = OFFSET+secondRow*ELEMENT_SIZE;//第2个元素的y坐标
            int step = firstY<secondY?4:-4;
            for (int i = 0; i < 15; i++) {
                try {
                    Thread.sleep(10);
                }catch (Exception e){

                }
                firstY +=step;
                secondY-=step;
                elements[firstRow][firstCol].setY(firstY);
                elements[secondRow][secondCol].setY(secondY);
                repaint();
            }
        }
    }


    /** 交换两个元素 */
    public void exchangeElement(){
        Element e1 = elements[firstRow][firstCol]; //获取第1个选中元素
        Element e2 = elements[secondRow][secondCol]; //获取第2个选中元素
        elements[firstRow][firstCol] = e2; //将第1个选中元素修改为第2个元素
        elements[secondRow][secondCol] = e1; //将第2个选中的元素修改为第1个元素
    }


    /** 扫描并删除元素 */
    public boolean eliminateElement(){
        boolean haveEliminated = false; //是否有元素被消除了
        for (int row=ROWS-1;row>=0;row--){
            for (int col=COLS-1;col>=0;col--){
                Element e = elements[row][col];
                if (e == null){
                    continue;
                }
                //1)查找一行中连续的个数。查找一列是连续的个数
                //2)将连续（可消除）的元素设计为可消除状态
                //2)将可消除状态的元素绘制爆炸动画
                //4)将可消状态元素设置为null 以等待其他元素的下落

                //1)查找一行中当前元素前面的连续的个数，查找一列中当前元素前面的连续的个数
                int colRepeat = 0;//行不变，列相邻--与当前元素相邻的行元素的连续重复个数
                for (int pc=col-1;pc>=0;pc--){ //倒着遍历当前元素前面的所有列
                    if (elements[row][pc] == null){
                        break;
                    }
                    //若遍历元素与当前元素的类型相同，则重复个数增1 否则break退出
                    if (elements[row][pc].getClass() == e.getClass()){
                        colRepeat++;
                    }else {
                        break; //只要有一个不同的 剩余的不再需要比较了
                    }
                }
                int rowRepeat = 0; //列不变 行相邻---与当前元素相邻的列元素的连续重复个数
                for (int pr=row-1;pr>=0;pr--){ //倒着遍历当前元素上面的所有行
                    if (elements[pr][col] == null){ //若上面的元素为null，则退出（后面的不比了）
                        break;
                    }
                    //若遍历元素与当前元素的类型相同，则重复个数增21，否则break退出（后面的不比了）
                    if (elements[pr][col].getClass()==e.getClass()){
                        rowRepeat++;
                    }else{
                        break;
                    }
                }

                //2)将连续（可消除）的元素设计为可消除状态
                if (colRepeat>=2){ //行不变 列相邻--若同一行相邻元素个数>=2 表示可消
                    elements[row][col].setEliminated(true); //设置当前元素可消
                    for (int i=1;i<=colRepeat;i++){ //遍历连续个数次
                        elements[row][col-i].setEliminated(true); //行不变 列前元素设置为可消
                    }
                }
                if (rowRepeat>=2){ //列不变 行相邻--若同一列相邻元素个数>=2 表示可消
                    elements[row][col].setEliminated(true); //遍历当前元素可消
                    for (int i = 1; i < rowRepeat; i++) {
                        elements[row-i][col].setEliminated(true); //列不变，行上元素设置为可消
                    }

                }


                //3)将可消除状态的元素绘制爆炸动画
                if (colRepeat>=2 || rowRepeat>=2){ //如果有可消除元素
                    for (int i=0;i<Images.bombs.length;i++){
                        repaint(); //重画 依次显示4张爆破图
                        try { //每爆一下休眠50毫秒
                            Thread.sleep(50);
                        }catch (Exception ex){

                        }
                    }

                }


                //4)将可消除元素设置为null 以等待其他元素的下落
                if (colRepeat>=2){ //行不变 列相邻
                    elements[row][col] = null; //将当前元素设置为null
                    for (int i=1;i<=colRepeat;i++){ //遍历连续个数次
                        elements[row][col-i] = null; //行不变 列前元素设置为null
                    }
                    haveEliminated = true;//有可消元素被消除了
                }
                if (rowRepeat>=2){ //列不变 行相邻
                    elements[row][col] = null; //当前元素设置为null
                    for (int i=1;i<=rowRepeat;i++){ //遍历连续个数次
                        elements[row-i][col] = null; //列不变 行上元素设置为null
                    }
                    haveEliminated = true; //有可消元素被消除了
                }

            }
        }
        return haveEliminated;
    }


    /** 下落元素 */
    public void dropElement(){
        for (int row = ROWS-1;row>=0;row--){ //遍历所有行（从下往上遍历）
            //只要有null元素就要将它上面的元素全部落下来，当没有null元素了则结束当前操作
            while (true){ //自造死循环
                int[] nullCols = {}; //当前行为null的列号
                for (int col=COLS-1;col>=0;col--){ //遍历所有列
                    Element e = elements[row][col]; //获取当前元素
                    if (e==null){ ///若当前列元素为null
                        nullCols = Arrays.copyOf(nullCols,nullCols.length+1);
                        nullCols[nullCols.length-1] = col;
                    }
                }

                if (nullCols.length>0){ //当前行有为null的列
                    //以4为步长  移动15次 正好移好（大小正好是60）
                    for (int count=0;count<15;count++){ //播放15帧动画
                        for (int i=0;i<nullCols.length;i++){ //所有null元素都要落
                            int nc = nullCols[i]; //获取null的列号
                            for (int dr=row-1;dr>=0;dr--){ //从当前行的上一行开始下落元素
                                Element e = elements[dr][nc]; //获取上方的元素
                                if (e!=null){
                                    e.setY(e.getY()+4); //设置元素y坐标为--本身y坐标+步长4
                                }
                            }
                        }
                        try {
                            Thread.sleep(10);
                        }catch (Exception e){

                        }
                        repaint();
                    }

                    //真正让数组上面的元素往下移动
                    for (int i = 0; i < nullCols.length; i++) {
                        int nc = nullCols[i]; //获取当前null列号
                        //从当前行开始，一直到最上面（不包括0，因为第1行要生成新元素），元素依次修改
                        for (int nr=row;nr>0;nr--){
                            elements[nr][nc] = elements[nr-1][nc]; //将当前元素赋值为它上一行的元素
                        }
                        elements[0][nc] = createElement(0,nc);//最上面生成新元素
                    }
                    /*
                      带数:假设6,2为null，意味着row=6,nc=2
                        nr=6  62=52
                        nr=5  52=42
                        nr=4  42=32
                        nr=3  32=22
                        nr=2  22=12
                        nr=1  12=02
                              02=新元素

                     */

                }else { //当前行没有null列 则break退出当前行处理 进行开始处理上一行
                    break;
                }

            }
        }
    }


    private boolean canInteractive = true; //可交互状态(默认可交互)
    private int selectedNumber = 0; //已经选中的元素个数
    private int firstRow = 0;  //第一个选中元素的ROW
    private int firstCol = 0;  //第一个选中元素的COL
    private int secondRow = 0; //第二个选中元素的ROW
    private int secondCol = 0; //第二个选中元素的COL

    /**
     * 启动程序
     */
    public void start() {
        fillAllElements();//填充所有元素
        MouseAdapter adapter = new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (!canInteractive){
                    return;
                }
                canInteractive = false;//可交互状态(默认可交互)
                int row = (e.getY()-OFFSET)/ELEMENT_SIZE;//获取选中元素的ROW
                int col = (e.getX()-OFFSET)/ELEMENT_SIZE;//获取选中元素的COL
                selectedNumber++;//选中元素的个数增1
                if (selectedNumber ==1 ){//第1次选中
                    firstRow = row; //记录第1个元素的Row
                    firstCol = col; //记录第1个元素的COL
                    elements[firstRow][firstCol].setSelected(true);//设置为选中状态
                    canInteractive = true; //可交互状态(默认可交互)
                }else if (selectedNumber == 2){
                    secondRow = row;
                    secondCol = col;
                    elements[secondRow][secondCol].setSelected(true);//设置为选中状态
                    if (isAdjacent()){//判断是否相邻
                        new Thread(){
                            public void run(){
                                elements[firstRow][firstCol].setSelected(false); //取消选中
                                elements[secondRow][secondCol].setSelected(false); //取消选中
                                moveElement();//移动两个元素（动画移动）
                                exchangeElement(); //交换两个元素
                                if (eliminateElement()){ //若有可消并消除了
                                    do {
                                        dropElement(); //下落元素
                                        try {
                                            Thread.sleep(10);
                                        }catch (Exception e1){

                                        }
                                    }while (eliminateElement()); //扫描有可消元素就继续消并下落
                                }else{ //没有可消 需要换回去
                                    moveElement(); //移动两个元素
                                    exchangeElement();//交换两个元素
                                }
                                canInteractive = true; //可交互

                            }
                        }.start();
                    }else{ //若不相邻
                        elements[firstRow][firstCol].setSelected(false);
                        elements[secondRow][secondCol].setSelected(false);
                        canInteractive = true; //可交互
                    }
                    selectedNumber =0; //选中个数归0
                }
                repaint();
            }

        };
        this.addMouseListener(adapter);
    }


    /** 重画方法 */
    public void paint(Graphics g) {
        Images.background.paintIcon(null,g,0,0); //画背景图
        for (int row = 0; row < ROWS; row++) {
            for (int col = 0; col < COLS; col++) {
                Element e = elements[row][col];
                if (e != null) {
                    e.paintElement(g);
                }
            }
        }
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        World world = new World();
        world.setFocusable(true);
        frame.add(world);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(WIDTH, HEIGHT + 17);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        world.start();
    }
}
